Chapter 2
MFC Dialogs, Controls, and Data Interaction

by Bill Heyman

In This Chapter

  Creating an Application 50
  Modifying the Application 57
  ToolTips 71
  Dialog Data Exchange 72
  Using Standard Dialog Boxes 74

This chapter describes the general techniques for creating dialog-based MFC applications using Microsoft Visual Studio. If you have already created MFC applications using AppWizard, you are probably already familiar with this material and might want to move to the more in-depth topics in later chapters.

Creating an Application

This section describes the MFC AppWizard supplied by Visual Studio. It leads you through the process of generating a new dialog-based MFC application and describes the basic features of the generated code.

Starting and Using MFC AppWizard

If you want, you can certainly derive your own classes from CWinApp and create your own MFC application from scratch. However, you can save yourself much effort by using the AppWizard, which automatically creates a lot of the common application code for you. The AppWizard gives you a jump start on getting into the actual coding of the business logic necessary for your application—without having to worry about the MFC infrastructure issues initially.

Starting the AppWizard is easy. In Microsoft Visual Studio, simply choose File, New from the menu. This displays the dialog shown in Figure 2.1, which gives you a choice of new projects to create.


Figure 2.1  The Projects tab of the New dialog displaying a list of the types of projects that you can create.

Because you want to create an MFC application, choose the project type entry named MFC AppWizard (exe). In the Location entry field, type the parent directory to contain this project (in Figure 2.1, e:\MyProjects was typed). Finally, in the Project Name, type the name of your project (in the Figure 2.1, MFCSample was typed). When you type the project name, the project location is automatically extended to have a subdirectory with the name of the project (in the Figure 2.1, this is how E:\MyProjects\MFCSample was created).


Note:  

Among the project types, you could also choose MFC AppWizard (dll). Choose this AppWizard if you want to package your MFC application in a dynamic link library to be used by other applications.


You might also note that the Create New Workspace radio button is automatically selected for you. A project contains all of the file references and compile and link options to allow you to create a single executable (EXE) or dynamic link library (DLL). When you have a more sophisticated application that has multiple DLLs and EXEs, you can associate all of your projects in an entity called a workspace. In the case of MFCSample, you are only creating a single executable—so a single workspace is appropriate. Choose OK to move to the start of the MFC AppWizard.


Note:  

You can add a project to an existing workspace at a later time, by opening that workspace. From the FileView tab, select the Workspace entry (on the top line), and right-click to display the context menu. Choose Insert Project into Workspace to display a dialog that allows you to select the project to add to the workspace.

Also, if you need to logically associate your EXE and DLL projects in multiple groupings, you can add your projects to more than one workspace.


The MFC AppWizard generates the initial code for your application, so you don’t have to code it by hand. Consequently, it guides you through the process of defining the general characteristics of your application, so it can generate code suited to your application’s needs. Figure 2.2 shows the MFC AppWizard - Step 1 dialog box.

The MFC AppWizard - Step 1 dialog box enables you to select whether your application uses the Single Document Interface (SDI), Multiple Document Interface (MDI), or is dialog-based. In addition, you choose whether or not your application uses the MFC document/view architecture, which separates the logic that displays your data to the user from logic that interacts with your data. Refer to Chapter 7, “The Document/View Architecture,” for more specific information on using the MFC document/view architecture. Because the document/view architecture is described later (and to keep things simple), the MFC Sample application is a dialog-based application. The document/view architecture is not available to dialog-based applications; hence, that check box is disabled. Click Next to proceed to the next MFC AppWizard step.


Figure 2.2  The first MFC AppWizard dialog box enables you to specify whether your application is a single document, multiple documents, or dialog-based.

SDI, MDI, and Dialog-Based Applications

A Single Document Interface (SDI) application is like the Paint accessory shipped with Windows. It has a single client area and often has a pull-down menu and toolbar. Use an SDI application when each instance of an application interacts with a single file or data and it displays data within a client area.

A Multiple Document Interface (MDI) application is like Microsoft Word. It has multiple client areas, but often has a single pull-down menu and toolbar. Use an MDI application when you want the user to have multiple documents open within the same application.

A dialog-based application is similar to the AppWizard within Visual Studio. It consists of a single form-based window with any of the various Windows controls, such as pushbuttons, list boxes, and entry fields. Use a dialog-based application when you do not need a client area in which to draw in your application or when your application is form-based and needs only the various Windows controls to interact with the user.

The MFC AppWizard - Step 2 of 4 dialog enables you to specify some of the features of the generated code. These include whether or not your application has an About box, WinSock capabilities, and ActiveX controls, and whether it supports automation. In addition, you can specify text that appears in your dialog’s title bar. This dialog is shown in Figure 2.3. Click Next to move to the next AppWizard step.


Figure 2.3  The Step 2 MFC AppWizard dialog enables you to add an About box and specify title bar text for your application.

Figure 2.4 shows the MFC AppWizard - Step 3 of 4 dialog. Using this dialog, you can specify whether you’d like your application to appear as an MFC Standard application or as a Windows Explorer-like application, with a tree view in one child window and a list view in another. In addition, you can indicate whether you’d like to link the MFC class library statically as part of your application—or have your application use the MFC dynamic link library. Click Next to move to the final step.


Figure 2.4  The Step 3 MFC AppWizard dialog allows you to choose to make your application appear like Windows Explorer.

The MFC AppWizard - Step 4 of 4 dialog is informational and requires no input from you. It lists the classes and source files that will be generated for your application. For the MFCSample project, Figure 2.5 shows the classes and files that are generated. Click Finish to complete the AppWizard process.


Note:  

Remember that if you link MFC as a DLL, your application’s installation routines must install the appropriate MFC DLLs needed by your application.



Figure 2.5  The Step 4 MFC AppWizard dialog shows you the classes and source files that AppWizard will generate for your application.

The AppWizard-Generated Code

After AppWizard has generated code for your MFC application, your current project will be loaded into the Visual Studio environment. You might be interested to know that, without any modification, you can compile and run this application. Of course, without the logic that you need to add, this application won’t do anything specific.

On the left side of the Visual Studio environment, there are three tabs containing different views of your application: FileView, ClassView, and ResourceView.



The FileView Tab

The rightmost tab, FileView, is shown in Figure 2.6 for the MFCSample application. There are five source files that are used to contain the definitions for the primary application classes. These files are MFCSample.cpp and MFCSampleDlg.cpp. The description of these files’ contents is discussed with the ClassView tab later in this section.


Figure 2.6  The FileView tab in Visual Studio for MFCSample.

Other files of interest include MFCSample.rc and Resource.h. The MFCSample.rc file contains the binary resource definitions of the icons, string table, version information, and dialogs for the application. Additionally, a file named Resource.h accompanies this file to define the numeric ids for each of the resources. The actual resources are discussed with the ResourceView tab later in this section.

Some of the more intriguing files that AppWizard generates for every MFC application are StdAfx.cpp and StdAfx.h. These are common files that are used to improve compile-time performance for MFC applications. Specifically, StdAfx.h is included by every source file first and contains the #include lines for each of the MFC class library headers. If you do this, this header file (and all of the MFC headers) can be parsed and precompiled into a binary form. Consequently, when subsequent source files are compiled, the compiler does not have to read in and parse all of the system header files. Thus, each of the source files can be compiled much faster.


Caution:  

Although each project can contain files of the same name (StdAfx.h and Resource.h), these files are likely to have different contents, particularly if the AppWizard options are different between projects.


The ClassView Tab

The leftmost tab, ClassView, for the MFCSample application is shown in Figure 2.7. The class view of your application lists all of the classes in it, as well as all of the methods and data members contained within each class. For the MFCSample application, three classes are generated: CAboutDlg, CMFCSampleApp, and CMFCSampleDlg.


Figure 2.7  The ClassView tab for MFCSample in Visual Studio.

The primary application class that AppWizard generated is named CMFCSampleApp. Declared in MFCSample.h and defined in MFCSample.cpp, this class is derived from CWinApp and represents the main class for your MFC application. See Chapter 1, “The MFC Architecture,” for more information on the CWinApp class.

The CMFCSampleDlg class represents the main window of the dialog-based MFCSample application. The CMFCSampleDlg class is derived from the CDialog class, and you can modify it to add the data and logic required for your application. This class is associated with a generated dialog resource, named IDD_MFCSAMPLE_DIALOG, which you can edit via the ResourceView tab. The CMFCSample class is declared in MFCSampleDlg.h and defined in MFCSampleDlg.cpp.

Likewise, the CAboutDlg class represents the About box in your application. You can add methods and data to this class if you need to modify the look and feel of the modal window that is displayed when Help, About is selected from your application’s pull-down menu. The CAboutDlg class is derived from CDialog and is associated with a generated dialog resource, named IDD_ABOUTBOX. The CAboutDlg class declaration is located in MFCSampleDlg.h and its definition is located in MFCSampleDlg.cpp.

The ResourceView Tab

The final tab available for browsing your application in the Visual Studio environment is the ResourceView tab. Shown in Figure 2.8, this tab enables you to browse and edit the binary resources associated with your application. The actual resources are contained in the file MFCSample.rc, and the numeric identifiers for the resources are stored in a file named Resources.h.


Figure 2.8  The ResourceView tab for MFCSample in Visual Studio.

The MFC AppWizard generated four different types of resources for your application: dialog, icon, string table, and version. Each of these types has one or more instances of that type of resource.

The dialog resource contains the layout and controls contained on your application’s dialog windows. MFC AppWizard generated two dialog resources for the MFC Sample application, named IDD_ABOUTBOX and IDD_MFCSAMPLE_DIALOG, representing the About box and the main window, respectively. By double-clicking on the dialog’s name (such as IDD_ABOUTBOX), you can edit and make changes to the layout of and controls within that dialog. By default, the About box dialog contains a generic icon, application name, and copyright string, and the main window dialog contains an OK and a Cancel pushbutton. You will certainly need to edit both of these dialogs before shipping your application.

MFCAppWizard also generated a graphical icon resource named IDR_MAINFRAME for the MFCSample application. The IDR_MAINFRAME icon is the image that appears in your application’s upper-left hand corner of the main frame, in its About box, and is associated with your executable.

The next resource type is the string table resource, which maps numeric identifiers to language-dependent strings. If you double-click on the string table resource, you can see that the MFC AppWizard generated a single string that can be used in a menu resource


Note:  

It is a good idea to isolate all of your language-dependent strings into a string resource table. In this way, your application code refers only to numeric identifiers, rather than literal strings. If you use string resources, when you need to translate your application to another language, the string tables can be translated to that language without change to your source code.


Finally, the MFC AppWizard generated a version resource named VS_VERSION_INFO for the MFCSample application. The version information contains both standard numeric information (such as a four-part version number, like 1.0.0.1, and an operating system flag on which the application can run, such as VOS__WINDOWS32) and standard and user-definable string information (such as an application description and copyright string). Utilities such as installation programs can use this information to compare one executable file to another to decide whether they need to replace an older version with a new one, for example.

Modifying the Application

At this point, you have a shell MFC dialog-based application called MFCSample. Of course, it contains no business logic, and the dialog is pretty boring with the lonely OK and Cancel buttons. If you want, you can run and debug it from the Visual Studio environment. Next, you can starting doing something interesting—like adding more controls and actually having the application do something.

Adding Dialog Controls

Certainly the first thing that you need to do is to add controls to your dialog. Otherwise, you cannot add code to handle and trigger events for the controls. Consequently, you’ll add a few basic controls to the MFCSample application to demonstrate some of the features of dialog-based MFC applications.

This chapter glosses over the actual functionality of the controls it describes and defers the in-depth analysis of control events and behavior to Chapter 3, “The Windows Common Controls.” However, this chapter does introduce you to the look and feel of the controls that you can add to your application.

In the MFCSample application, there are simply some pushbuttons that demonstrate some of the standard dialogs available to you as well as some entry fields and static text fields to demonstrate Dialog Data Exchange (DDX). The MFCSample main window is shown in Figure 2.9.


Figure 2.9  The MFCSample main window containing pushbuttons and other controls.



Dialog Control Types

A control is a specialized GUI object that can allow passive or active user interaction with the application. Controls usually serve a limited number of purposes and provide an intuitive means of the user interacting with those purposes. Controls can be sorted in three basic categories: information presentation, information request, and information modifiers. Some controls can be classified in only one category, whereas others could be classified in more than one category.

An information presentation type of control simply displays data to the user. Perhaps the most basic information presentation controls are the static text and the picture controls. Other controls such as the tree control and the list box can be classified in the other two categories—but, in any case, still present information to the end user.

The second type of control is the information request control, which enables the user to obtain more information from the application. The most basic control serving this purpose is the button control. However, depending on how the application is written, a tree control and list box could be in this category also; namely, where an application adds more data to the tree control when a node is expanded and a list box item is double-clicked.

Finally, the last type of control is the information modifier, which enables the user to change the application’s data. The edit box, radio button, and check box are the most basic controls that serve this purpose. Alternatively, the list control can be used to serve this purpose if it allows data to be edited within its cells.

Furthermore, three classifications of Windows controls are available to you through the Visual Studio resource editor palette. These are the standard controls, the common controls, and the custom controls.

Standard Controls

The standard controls are the classic controls that have been a part of Windows since its earliest days. These are the most basic controls that all Windows users know and love. These include buttons, list boxes, check boxes, radio buttons, and scrollbars. In the subsequent sections, the behavior and use of each of these controls is discussed briefly. Again, refer to Chapter 3 for a more in-depth discussion of the methods and events for each of these controls that are programmatically available to your application.

Picture

The picture control represents a set of information presentation controls available to your application. This control can contain an icon, a bitmap, a metafile, a rectangular outline (called a frame), or a filled rectangle, depending on the style flag set at design time. Use this control to display an image or draw a filled or outlined rectangle in your dialog. Figure 2.10 shows an example of a picture control with the default MFC icon in it.


Figure 2.10  A picture control with the default MFC icon in it.

Static Text

The static text control is another information presentation control. It simply displays a line of text to the user—however, the user typically does not directly interact with this text in any way. This control simply displays text to the user. Figure 2.11 shows an example of a static text control.


Figure 2.11  A static text control with an output-only message in it.

Edit Box

The edit box control is very similar to the static text control, except that the user can actually edit the textual data contained within it. Use this type of control when you want the user to edit a single line or multiple lines of textual data. It is considered to be both an information presentation and an information modifier type of control. Figure 2.12 shows an example of an edit box control.


Figure 2.12  An edit box control just waiting for its text to be modified.

Group Box

The group box control is an information presentation control that provides simply a way of grouping other controls. Group boxes are often used to contain a set of radio buttons that provide a mutually exclusive choice. Figure 2.13 demonstrates this case.


Figure 2.13  A group box control with a set of mutually exclusive radio button choices within it.

Button

The button control (also known as a pushbutton) is an information request control. Use this control to enable your application to respond to a user-triggered event. Perhaps the most common control on dialogs is a button named Cancel that typically dismisses the dialog without saving its data. Figure 2.14 shows a Cancel button.


Figure 2.14  A button control named Cancel.

Check Box

The check box control is often used as an information modifier. It can represent either two or three states; two states are true and false and three states are true, false, and indeterminate. Use this control when you’d like the user to choose a Boolean state. Figure 2.15 shows a check box.


Figure 2.15  A check box control enables the user to specify his or her preferences.

Radio Button

The radio button control is another information modifier. Rarely used in isolation, this control is often part of a group of mutually exclusive choices. Use a group of these controls when you’d like the user to choose one of a finite number of selections that are known at application design time. Figure 2.13, earlier in the chapter, shows such a group of radio buttons.v

Combo Box

Like the radio button, the combo box control is another information modifier. It provides a similar purpose to a group of radio buttons, except the finite number of selections can be specified at runtime. Using the combo box, the user can choose one of its entries. There are three types of combo box available to you: simple, drop-down, and drop-list. The simple and drop-list combo boxes are different only in visual appearance. The drop-down combo box allows the user to enter a new entry in the edit box portion of the control. Figures 2.16 and 2.17 demonstrate each of these types.


Figure 2.16  A simple combo box control.


Figure 2.17  A drop-down or drop-list combo box control.

List Box

The list box control is another information modifier. It displays a list of choices to the user—and, depending on its actual style, the user can choose either none, one, or many of its items. With a single selection list box, only one entry can be selected, which makes its behavior similar to that of a simple combo box. Figure 2.18 shows a list box.


Figure 2.18  A list box control.

Horizontal and Vertical Scrollbars

The scrollbar control is often used to enable the user to move a graphical image or text within the current view. Additionally, it can enable the user to select a contiguous range within a finite set, similar to the Slider control (described following). Figures 2.19 and 2.20 show a horizontal and a vertical scrollbar, respectively.


Figure 2.19  A horizontal scrollbar control.


Figure 2.20  A vertical scrollbar control.



Common Controls

As Windows matured and the user-interaction needs of applications increased, Microsoft introduced a set of advanced controls for dialogs. Called the “common” controls, they were shipped as a separate, redistributable DLL and were not part of any standard Windows installation until Windows 95 and Windows NT 4. These controls include the spin control, progress indicator, slider, hot key, list control, tree control, tab control, animation control, rich text edit, date/time picker, month calendar, and IP address control.

Spin

The spin control (sometimes called a spin button or an up-down control) allows the user to increment or decrement a value in an associated control. Consequently, it is an information modifier. This control allows the user to fine-tune the data in another control and is often used to allow the user to scroll through a list of numeric values. The spin control is shown in Figure 2.21.

Progress

The progress indicator control is an information presentation control that displays a graphical representation of the percent progress. Use this control when you’d like your application to indicate graphically the current amount of progress. Figure 2.22 demonstrates this control.


Figure 2.21  A spin control.


Figure 2.22  A progress indicator control.

Slider

The slider control allows the user to select one of a range of values. It is similar to a level control on a stereo’s equalizer. You can choose to have either horizontal or vertical sliders. This control is shown in Figure 2.23.


Figure 2.23  A horizontal slider control with a point and tick marks.

Hot Key

The hot key control enables the user to enter a combination keystroke that includes Alt, Shift, or Ctrl, and another key to define a hot key to the application. Use this control when you let the user define a keystroke combination for the application. Figure 2.24 demonstrates this control.


Figure 2.24  A hot key control.

List Control

The list control is essentially the classic list box on steroids. It allows one of four views: large icon (single column with text), small icon (single column with text), list (multi-column, small icon with text), and report (single column, small icon with text and other textual details in columns). Figure 2.25 shows a list control in report view.


Figure 2.25  A list control in report view.

Tree Control

The tree control provides a means of displaying a hierarchy of data within your application. The standard Windows Explorer window is a tree control, showing a hierarchy of drives, folders, and files. The tree control is shown in Figure 2.26.


Figure 2.26  A tree control showing a hierarchy of data.

Tab Control

The tab control provides a means of logically organizing your application’s data in your user interface. If you use this control, your GUI needs to display only the controls in which the user is interested—rather than overwhelming him with a large number of controls at once. Figure 2.27 shows a tab control.


Figure 2.27  A tab control.

Animation

The animation control enables you to display an AVI file within the control’s area when clicked. Use this control when you have multimedia graphics that you’d like displayed within your dialog.

Rich Edit

The rich edit control enables you to embed more sophisticated text processing in your application, so you can provide the user a means of formatting text and paragraphs, as well as loading and saving the text in RTF format. This control is shown in Figure 2.28.


Note:  

You must call AfxInitRichEdit in your application class’s InitInstance method to use the rich edit control.



Figure 2.28  A rich edit control.

Date/Time Picker

The date/time picker control provides a means of allowing the user to select a date or time through your user interface. Its visual appearance can be similar to a combo box or an edit text/spin control combination. The latter combination is shown in Figure 2.29.


Figure 2.29  A date/time picker control using a spin control.

Month Calendar

The month calendar control allows your application to display an entire calendar month to the end user. It provides an intuitive way for the user to interact with dates in your application. The month calendar control is shown in Figure 2.30.


Figure 2.30  The month calendar control.

IP Address

The IP address control provides a straightforward means of displaying and allowing the user to modify an Internet network address in a way consistent with the rest of the system. This control is shown in Figure 2.31.


Figure 2.31  An IP address control.

Extended Combo Box

The extended combo box control has the same behavior and types as the standard combo box control. However, unlike the standard control, it supports the use of images—without requiring an owner draw routine to draw them. Use this control if you need to allow the user to select one of a set of items with images.

Custom Controls

Another type of control that you can add to your dialog is called a custom control. You can create your own controls to satisfy specific requirements for your application. Refer to Chapter 5, “Custom Control Development,” for information on how to create and use custom controls.

Adding Initialization

There are two locations where dialog initialization is typically performed: in the constructor and in the OnInitDialog method for your dialog class. The actual location for your particular initialization code depends on what is required at the time of initialization.

Use the dialog class constructor to initialize member variables that have no dependence on any controls on the dialog. For example, the constructor is an excellent place to initialize any class pointers to zero. Listing 2.1 demonstrates how the constructor is used in the MFCSample application to initialize the m_currentColor member variables.

Listing 2.1 Initialization of Class Members in the Constructor


CMFCSampleDlg::CMFCSampleDlg(CWnd* pParent /*=NULL*/)
   : CDialog(CMFCSampleDlg::IDD, pParent)
   , m_currentColor(RGB(0,0,255))
{
   //{{AFX_DATA_INIT(CMFCSampleDlg)
   m_fileName = _T(“”);
   //}}AFX_DATA_INIT

   // [omitted code]
}

Listing 2.1 also shows the initialization of a member variable, m_fileName, used in DDX. This variable represents the filename that is displayed in the MFCSample application’s main window, as shown earlier in Figure 2.9.


Note:  

The initialization of m_fileName is contained within a set of comments that are tagged with AFX_DATA_INIT. These comments are flags that indicate to the Visual Studio environment that the code between the comments was generated. As a general rule, you typically leave this code alone and do not put your own code within these comments, for fear of losing it at a later date.


The second (and perhaps most common) location of initialization code for your dialog is in the OnInitDialog method. MFC calls OnInitDialog after the dialog window and all of its controls have been created. Consequently, feel free to interact with any of the controls in your dialog window in your dialog class’s OnInitDialog method. Listing 2.2 shows the calls to EnableToolTips and DrawBitmapWithColor in initialization section for the MFCSample application.

Listing 2.2 Dialog Initialization in the OnInitDialog Method


BOOL CMFCSampleDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    // [omitted code]

    // TODO: Add extra initialization here
    EnableToolTips();
    DrawBitmapWithColor();

    return TRUE;  // return TRUE  unless you set the focus to a control
}




Note:  

In general, insert your application’s initialization code after the Visual Studio-generated comment, “TODO: Add extra initialization here.”


Using Dialog Controls

After you’ve created your dialog class, you’ll most certainly need to programmatically interact with and receive events from the controls in your dialog. The next section describes exactly how to do just that.

Interacting with Controls

To interact with your dialog’s controls, you’ll need instances of classes for each of your dialog’s controls that you need to manipulate. There are two basic ways of creating an instance of an object class. The first way is by using the Visual Studio ClassWizard to define a member variable that is associated with your dialog’s control. The second way is to create an MFC control class object on-the-fly.

To create an instance of an object class using Class Wizard, you must first open the ClassWizard dialog by selecting View, ClassWizard from the Visual Studio pull-down menu. When the ClassWizard dialog appears, choose the tab named Member Variables. The ClassWizard open to this tab is shown in Figure 2.32.


Figure 2.32  Using the ClassWizard to view the current member variables associated with dialog controls.

Next, select the control for which you need a member variable defined and click Add Variable. At this point, the Add Member Variable dialog appears as shown in Figure 2.33. Enter a name for your variable, choose the Control category, and select the type for your variable. When you click OK, this member variable is added automatically to your class.


Note:  

Some controls might have additional categories beyond Control—usually types such as CString and int. These types of member variables are used with Dialog Data Exchange, which is explained later in this chapter.


Now that the member variable is defined, you can manipulate the control as required. Hence, to disable a button named m_pbOK, code the following:

m_pbOK.EnableWindow(FALSE);


Figure 2.33  Using the ClassWizard to define a member variable for a dialog control.

The methods that you use are limited to the methods available to you for the control type that you specified when you created the member variable.

The second way of interacting with a dialog control is by creating an automatic variable directly within your code. To do this, you must first create an automatic instance of the class appropriate for your control. Next, you mt obtain a pointer to a CWnd object for your dialog’s control using the GetDlgItem method. Listing 2.3 demonstrates the creation of an automatic variable named pbOK that is associated with the IDOK control in your dialog.

Listing 2.3 On-the-Fly Control Variable Creation


CButton *pbOK = DYNAMIC_DOWNCAST(CButton, GetDlgItem(IDOK))


Caution:  

Because the pointer returned from GetDlgItem is temporary and is not owned by your application, do not store or delete it.


Receiving Events from Controls

Each type of control (CButton, CListCtrl, and so on) has its own set of events that you can handle in your application. With the Visual Studio environment, it is straightforward to add event handlers to your application.

The MFCSample application sets up several event handlers to perform different actions. For example, each button on its dialog has an event handler to perform an action when the user pushes it. To create an event handler for a button click, you can use ClassWizard to help you quickly add it to your class.

First, from the Visual Studio pull-down menu, select View, ClassWizard. When the ClassWizard window opens, select the Message Maps tab, if that tab is not already on top. Figure 2.34 shows this tab for the MFCSample application.


Figure 2.34  Using the ClassWizard to view the current message maps associated with dialog controls.

Next, select the class owning a control for which you’d like to set up a message map in the Class Name combo box. After you choose the class, a list of the objects for which you can set up a message map is shown in the Object IDs list box. By selecting the various Object IDs, you can see which events can be handled for that object. Additionally, the events that are in bold in the Messages list box already have a handler defined.

To add a new event handler, choose the appropriate message in the Messages list box and click the Add Function button. At this point, you are prompted for the name of the function to add, as shown in Figure 2.35. When you click OK to name the function, Visual Studio generates code within your application to handle that event.


Figure 2.35  Using the ClassWizard to name a member function to handle a dialog control event.

Finally, when you click Edit Code, Visual Studio displays the handler method for that event and allows you to edit it. Listing 2.4 shows the handler method for the File button in the MFCSample application.

Listing 2.4 Handling a File Pushbutton Click in MFCSample


void CMFCSampleDlg::OnPbFile()
{
   // TODO: Add your message handler code here and/or call default
}

ToolTips

ToolTips are small, yellow text windows that appear over certain dialog controls after the mouse pointer stops for a moment. They provide a mechanism for you to give the user more detailed information about how that control is used. By adding ToolTips to your application, you can make it more user-friendly and usable.

Enabling ToolTips

To use ToolTips, you must first enable them for your dialog window by calling EnableToolTips. An ideal place for doing this is in your OnInitDialog method, as shown earlier in Listing 2.2. After you’ve done this, your application receives TTN_NEEDTEXT notification messages when a ToolTip window is about to be displayed by Windows.


Note:  

If you need to disable ToolTips in your application, call CancelToolTips.


Displaying Text

The ToolTip notification message, TTN_NEEDTEXT, gives your application the opportunity to provide the text that needs to appear in the ToolTip window. Follow these steps to add a handler for this message:

1.  Add the following line to your message map declaration for your class in its declaration in the header file:
afx_msg BOOL OnNeedToolTipText(UINT id, NMHDR * pTTTStruct,
                               LRESULT * pResult);
2.  Add the following line to your class’s message map definition in the source file between BEGIN_MESSAGE_MAP and END_MESSAGE_MAP:
ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnNeedToolTipText)
3.  Add a definition of the OnNeedToolTipText method that you just declared in your class’s header file and message map. Listing 2.5 shows the definition of this method for MFCSample.



Listing 2.5 Handling of ToolTips in MFCSample


BOOL CMFCSampleDlg::OnNeedToolTipText(UINT id, NMHDR * pNMHdr,
                                      LRESULT * pResult)
{
   TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *) pNMHdr;

   if (pTTT->uFlags & TTF_IDISHWND) {
      UINT idCtrl = ::GetDlgCtrlID(HWND(pTTT->hdr.idFrom));

      if (idCtrl == IDC_PB_CUSTOM_MODELESS && m_custDlg.m_hWnd) {
         pTTT->lpszText = MAKEINTRESOURCE(IDS_TT_PB_CUSTOM_MODELESS2);
      }
      else {
         pTTT->lpszText = MAKEINTRESOURCE(idCtrl);
      }

      pTTT->hinst    = AfxGetResourceHandle();

      return TRUE;
   }

   return FALSE;
}

Listing 2.5 demonstrates how to add custom logic to dynamically change the actual ToolTip text according to the current state of the application. In MFCSample, generally all ToolTips are loaded from string resources where the string resource identifier is equal to the control identifier. However, if a modeless dialog is currently displayed, the ToolTip for the custom modeless pushbutton changes accordingly.

Dialog Data Exchange

Dialog Data Exchange is a powerful means of associating non-control datatypes with controls. When DDX is used, your application logic can deal with the data itself, rather than the controls to get and set the data.

The “Interacting with Controls” section discussed associating a control-type member variable with a control. With DDX, you can associate a data member variable with a control. For example, rather than having an m_edMyText CEditBox member variable, you can have an m_myText CString member variable. Furthermore, using a DDX method named UpdateData, your application can always ensure that the data member variable and actual control remain in sync.

Standard DDX

When you create a non-control member variable that is associated with a control, Visual Studio generates code to initialize and associate the data member and the control. The MFCSample application has one member variable, m_fileName, which is associated with a static text control to display its data.

Upon creating the member variable (as discussed in “Interacting with Controls”), Visual Studio creates initialization code for those data members in the class’s constructor, as shown earlier in Listing 2.1.

Additionally, it creates a method named DoDataExchange that exchanges the data between the control and the data member (and vice versa). For the MFCSample application, this method is shown in Listing 2.6.

Listing 2.6 DDX Method DoDataExchange in MFCSample


void CMFCSampleDlg::DoDataExchange(CDataExchange* pDX)
{
   CDialog::DoDataExchange(pDX);
   //{{AFX_DATA_MAP(CMFCSampleDlg)
   DDX_Control(pDX, IDC_ST_COLOR, m_stColor);
   DDX_Control(pDX, IDC_PB_CUSTOM_MODELESS, m_pbCustomModeless);
   DDX_Control(pDX, IDC_ST_FONT, m_stFont);
   DDX_Text(pDX, IDC_ST_FILE, m_fileName);
   //}}AFX_DATA_MAP
}

In the DoDataExchange method for the CMFCSampleDlg class, four DDX associations are implemented. The first three associate the dialog control with the control-type member variable that references it. In this way, IDC_ST_COLOR is associated with m_stColor, IDC_PB_CUSTOM_MODELESS is associated with m_pbCustomModeless, and IDC_ST_FONT is associated with m_stFont. The fourth association is between a control and a data member variable: IDC_ST_FILE and m_fileName.

UpdateData

The UpdateData method is what keeps all of the DDX associations in sync. Using this method, you can force the data to go either from the control to the member variable or vice versa, based upon the passed parameter.

This UpdateData method is declared as follows in the CWnd class:

BOOL UpdateData(BOOL bSaveAndValidate = TRUE);

If you pass TRUE as the parameter to UpdateData, the data moves from the controls to their associated data members. Passing FALSE does the reverse. If an error occurs due to a data validation error, FALSE is returned from this method.

Using Standard Dialog Boxes

MFC provides four standard dialog boxes that allow your application to get information from the user in a way consistent with other applications. These dialogs are the file open/save dialog, the color selector, the font selector, and the print configuration dialog.

File Open/Save

The MFC CFileDialog class represents the File Open/Save dialog. By instantiating an object from this class, your application can prompt the user for a filename to open or save. The File Open dialog is shown in Figure 2.36.


Figure 2.36  The Windows standard File Open dialog.

The MFCSample application displays the File Open dialog when the user clicks the File button. If the user selects a file and clicks OK, that file is displayed in the main dialog. The code to support this use is shown in Listing 2.7.

Listing 2.7 Simple Code to Display the File Open Dialog


void CMFCSampleDlg::OnPbFile()
{

   CFileDialog fileDlg(TRUE);

   if (fileDlg.DoModal() == IDOK) {
      m_fileName = fileDlg.GetFileName();
      UpdateData(FALSE);
   }
}

Color Selector

The MFC CColorDialog class represents the Color selection dialog. By instantiating an object from this class, your application can prompt the user for a color. The Color selection dialog is shown in Figure 2.37.


Figure 2.37  The Windows standard Color selection dialog.

The MFCSample application displays the Color Selection dialog when the user clicks the Color button. If the user selects a color and clicks OK, that color is displayed in the main dialog. The code to support this use is shown in Listing 2.8.

Listing 2.8 Simple Code to Display the Color Selection Dialog


void CMFCSampleDlg::OnPbColor()
{

   CColorDialog colorDlg(m_currentColor);

   if (colorDlg.DoModal() == IDOK) {
     m_currentColor = colorDlg.GetColor();
     DrawBitmapWithColor();
   }
}

Font Selector

The MFC CFontDialog class represents the Font selection dialog. By instantiating an object from this class, your application can prompt the user for a font face, size, and style. The Font selection dialog is shown in Figure 2.38.

The MFCSample application displays the Font Selection dialog when the user clicks the Font butn. If the user selects a font face, size, and style and clicks OK, that font is used to draw some sample text that is displayed in the main dialog. The code to support this use is shown in Listing 2.9.


Figure 2.38  The Windows standard Font selection dialog.

Listing 2.9 Simple Code to Display the Font Selection Dialog


void CMFCSampleDlg::OnPbFont()
{

   CFontDialog fontDlg;

   if (fontDlg.DoModal() == IDOK) {
      CFont   font;
      LOGFONT logFont;

      fontDlg.GetCurrentFont(&logFont);
      font.CreateFontIndirect(&logFont);

      m_stFont.SetFont(&font);
   }
}

Print Dialog

The MFC CPrintDialog class represents the Print selection dialog. By instantiating an object from this class, your application can prompt the user for a printer. The Print selection dialog is shown in Figure 2.39.

The MFCSample application displays the Printer Selection dialog when the user clicks the Print button. If the user selects a printer and clicks OK, a sample page is printed on that printer. The code to support this use is shown in Listing 2.10.

Listing 2.10 Simple Code to Display the Printer Selection Dialog


void CMFCSampleDlg::OnPbPrint()
{
   CPrintDialog printDlg(FALSE);

   if (printDlg.DoModal() == IDOK) {
      CDC printerDC;

      printerDC.Attach(printDlg.GetPrinterDC());

      int cx = printerDC.GetDeviceCaps(PHYSICALWIDTH);
      int cy = printerDC.GetDeviceCaps(PHYSICALHEIGHT);

      // add printing logic here...
      printerDC.StartDoc(“MFCSample Document”);
      printerDC.StartPage();

      DrawClock(printerDC, __min(cx, cy));

      printerDC.EndPage();
      printerDC.EndDoc();

      CString msg = “Printed to: ”;
      msg += printDlg.GetDeviceName();
      MessageBox(msg);
   }
}


Figure 2.39  The Windows standard Print selection dialog.

Summary

Microsoft Visual Studio’s AppWizard makes creating a simple dialog-based MFC application easy. After you have followed the AppWizard through its steps and have generated an application shell, you can add your application’s functionality in a relatively straightforward manner.

Additionally, the Visual Studio resource editor enables you to add standard controls to your application—from basic controls such as pushbuttons and list boxes to the newer, more specialized controls such as the hot key control and IP address control. You can use one of standard selection dialogs to prompt the user for a file, printer, color, and font at the appropriate places within your application.

The MFC architecture provides a standard mechanism, Dynamic Data Exchange, for interchanging data between variables and your dialog’s controls. When equipped with DDX support, your application’s dialogs can validate and transfer user input to your dialog in a well-designed manner.

For more information on programming the dialog controls, see Chapter 3.